<!DOCTYPE html>
<html lang="zh-Hans">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.2.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/vhuichen.ico">
  <link rel="icon" type="image/png" sizes="32x32" href="/vhuichen.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/vhuichen.ico">
  <link rel="mask-icon" href="/vhuichen.ico" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"vhuichen.github.io","root":"/","images":"/images","scheme":"Pisces","darkmode":false,"version":"8.13.0","exturl":false,"sidebar":{"position":"left","display":"display","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Searching...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="初始化流程- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;初始化入口 - (void)_SR_commonInit;队列之类的数据初始化。这里要说的是，">
<meta property="og:type" content="article">
<meta property="og:title" content="SRWebSocket 源码解析">
<meta property="og:url" content="https://vhuichen.github.io/2021/10/NetworkProtocol/SRWebSocket/SRWebSocket/index.html">
<meta property="og:site_name" content="vhuichen">
<meta property="og:description" content="初始化流程- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;初始化入口 - (void)_SR_commonInit;队列之类的数据初始化。这里要说的是，">
<meta property="og:locale">
<meta property="article:published_time" content="2021-10-12T16:00:00.000Z">
<meta property="article:modified_time" content="2021-10-23T16:00:00.000Z">
<meta property="article:author" content="vhuichen">
<meta property="article:tag" content="WebSocket">
<meta property="article:tag" content="SRWebSocket">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://vhuichen.github.io/2021/10/NetworkProtocol/SRWebSocket/SRWebSocket/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-Hans","comments":true,"permalink":"https://vhuichen.github.io/2021/10/NetworkProtocol/SRWebSocket/SRWebSocket/","path":"2021/10/NetworkProtocol/SRWebSocket/SRWebSocket/","title":"SRWebSocket 源码解析"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>SRWebSocket 源码解析 | vhuichen</title>
  






  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">vhuichen</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">记录、分享</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section">Home</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section">Archives</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section">About</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E6%B5%81%E7%A8%8B"><span class="nav-number">1.</span> <span class="nav-text">初始化流程</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#id-initWithURLRequest-NSURLRequest-request-protocols-NSArray-protocols-allowsUntrustedSSLCertificates-BOOL-allowsUntrustedSSLCertificates"><span class="nav-number">1.0.1.</span> <span class="nav-text">- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#void-SR-commonInit"><span class="nav-number">1.0.2.</span> <span class="nav-text">- (void)_SR_commonInit;</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#void-initializeStreams"><span class="nav-number">1.0.3.</span> <span class="nav-text">- (void)_initializeStreams;</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BF%9E%E6%8E%A5%E6%B5%81%E7%A8%8B"><span class="nav-number">2.</span> <span class="nav-text">连接流程</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#void-open"><span class="nav-number">2.0.1.</span> <span class="nav-text">- (void)open;</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#void-openConnection"><span class="nav-number">2.0.2.</span> <span class="nav-text">- (void)openConnection;</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#void-stream-NSStream-aStream-handleEvent-NSStreamEvent-eventCode"><span class="nav-number">2.0.3.</span> <span class="nav-text">- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#void-didConnect"><span class="nav-number">2.0.4.</span> <span class="nav-text">- (void)didConnect;</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#void-readHTTPHeader"><span class="nav-number">2.0.5.</span> <span class="nav-text">- (void)_readHTTPHeader;</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#void-HTTPHeadersDidFinish"><span class="nav-number">3.</span> <span class="nav-text">- (void)_HTTPHeadersDidFinish;</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E6%B5%81%E6%8E%A5%E6%94%B6%E6%B5%81%E7%A8%8B"><span class="nav-number">4.</span> <span class="nav-text">数据流接收流程</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#void-safeHandleEvent-NSStreamEvent-eventCode-stream-NSStream-aStream"><span class="nav-number">4.0.1.</span> <span class="nav-text">- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#BOOL-innerPumpScanner"><span class="nav-number">5.</span> <span class="nav-text">- (BOOL)_innerPumpScanner</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E8%AF%BB%E5%8F%96%E6%B5%81%E7%A8%8B"><span class="nav-number">6.</span> <span class="nav-text">数据读取流程</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#void-readFrameNew"><span class="nav-number">6.0.1.</span> <span class="nav-text">- (void)_readFrameNew;</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#void-readFrameContinue"><span class="nav-number">6.1.</span> <span class="nav-text">- (void)_readFrameContinue;</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE%E6%B5%81%E7%A8%8B"><span class="nav-number">7.</span> <span class="nav-text">发送数据流程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">8.</span> <span class="nav-text">总结</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="vhuichen"
      src="/images/vhuichen.jpg">
  <p class="site-author-name" itemprop="name">vhuichen</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">78</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">11</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">86</span>
        <span class="site-state-item-name">tags</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="Back to top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://vhuichen.github.io/2021/10/NetworkProtocol/SRWebSocket/SRWebSocket/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/vhuichen.jpg">
      <meta itemprop="name" content="vhuichen">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="vhuichen">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="SRWebSocket 源码解析 | vhuichen">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          SRWebSocket 源码解析
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2021-10-13 00:00:00" itemprop="dateCreated datePublished" datetime="2021-10-13T00:00:00+08:00">2021-10-13</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">Edited on</span>
      <time title="Modified: 2021-10-24 00:00:00" itemprop="dateModified" datetime="2021-10-24T00:00:00+08:00">2021-10-24</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">In</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/" itemprop="url" rel="index"><span itemprop="name">网络协议</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h3 id="初始化流程"><a href="#初始化流程" class="headerlink" title="初始化流程"></a>初始化流程</h3><h5 id="id-initWithURLRequest-NSURLRequest-request-protocols-NSArray-protocols-allowsUntrustedSSLCertificates-BOOL-allowsUntrustedSSLCertificates"><a href="#id-initWithURLRequest-NSURLRequest-request-protocols-NSArray-protocols-allowsUntrustedSSLCertificates-BOOL-allowsUntrustedSSLCertificates" class="headerlink" title="- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;"></a>- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;</h5><p>初始化入口</p>
<h5 id="void-SR-commonInit"><a href="#void-SR-commonInit" class="headerlink" title="- (void)_SR_commonInit;"></a>- (void)_SR_commonInit;</h5><p>队列之类的数据初始化。<br>这里要说的是，SRWebSocket 内部创建一个常驻线程，用来接收数据流，还有一个专门用来处理业务的队列；当没有数据传输时常驻线程会进入休眠，此时队列任务发现<code>_readBuffer</code>没有数据，也会跳出循环等待，这样没有数据时也就不需要消耗多少资源了。</p>
<h5 id="void-initializeStreams"><a href="#void-initializeStreams" class="headerlink" title="- (void)_initializeStreams;"></a>- (void)_initializeStreams;</h5><p>初始化输入输出流</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">- (void)_initializeStreams &#123;</span><br><span class="line">    uint32_t port = _url.port.unsignedIntValue;</span><br><span class="line">    if (port == 0) &#123;</span><br><span class="line">        if (!_secure) &#123;</span><br><span class="line">            port = 80;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            port = 443;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    NSString *host = _url.host;</span><br><span class="line">    </span><br><span class="line">    CFReadStreamRef readStream = NULL;</span><br><span class="line">    CFWriteStreamRef writeStream = NULL;</span><br><span class="line">    //将 host port 与输入输出流绑定在一起</span><br><span class="line">    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &amp;readStream, &amp;writeStream);</span><br><span class="line">    </span><br><span class="line">    _outputStream = CFBridgingRelease(writeStream);</span><br><span class="line">    _inputStream = CFBridgingRelease(readStream);</span><br><span class="line">    </span><br><span class="line">    _inputStream.delegate = self;</span><br><span class="line">    _outputStream.delegate = self;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="连接流程"><a href="#连接流程" class="headerlink" title="连接流程"></a>连接流程</h3><h5 id="void-open"><a href="#void-open" class="headerlink" title="- (void)open;"></a>- (void)open;</h5><p>连接入口</p>
<h5 id="void-openConnection"><a href="#void-openConnection" class="headerlink" title="- (void)openConnection;"></a>- (void)openConnection;</h5><ol>
<li>将输入输出流注册到常驻线程</li>
<li>开启输入输出流</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[_outputStream scheduleInRunLoop:aRunLoop forMode:mode];</span><br><span class="line">[_inputStream scheduleInRunLoop:aRunLoop forMode:mode];</span><br><span class="line">[_outputStream open];</span><br><span class="line">[_inputStream open];</span><br></pre></td></tr></table></figure>
<span id="more"></span>
<h5 id="void-stream-NSStream-aStream-handleEvent-NSStreamEvent-eventCode"><a href="#void-stream-NSStream-aStream-handleEvent-NSStreamEvent-eventCode" class="headerlink" title="- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;"></a>- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;</h5><p>打开流成功后的回调</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode &#123;</span><br><span class="line">    __weak typeof(self) weakSelf = self;</span><br><span class="line">    //第一次进来没认证，则进入，认证一次</span><br><span class="line">    if (_secure &amp;&amp; !_pinnedCertFound &amp;&amp; (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) &#123;</span><br><span class="line">        NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];</span><br><span class="line">        if (sslCerts) &#123;</span><br><span class="line">            SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust];</span><br><span class="line">            if (secTrust) &#123;</span><br><span class="line">                NSInteger numCerts = SecTrustGetCertificateCount(secTrust);</span><br><span class="line">                for (NSInteger i = 0; i &lt; numCerts &amp;&amp; !_pinnedCertFound; i++) &#123;</span><br><span class="line">                    SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i);</span><br><span class="line">                    NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert));</span><br><span class="line">                    for (id ref in sslCerts) &#123;</span><br><span class="line">                        SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref;</span><br><span class="line">                        NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));</span><br><span class="line">                        if ([trustedCertData isEqualToData:certData]) &#123;</span><br><span class="line">                            _pinnedCertFound = YES;</span><br><span class="line">                            break;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            if (!_pinnedCertFound) &#123;</span><br><span class="line">                return;//异常</span><br><span class="line">            &#125; else if (aStream == _outputStream) &#123;</span><br><span class="line">                // 认证成功，如果是输出流，则认为流连接成功，开始HTTP连接</span><br><span class="line">                dispatch_async(_workQueue, ^&#123;</span><br><span class="line">                    [self didConnect];</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    dispatch_async(_workQueue, ^&#123;</span><br><span class="line">    		// 后面所有的流都在这里处理</span><br><span class="line">        [weakSelf safeHandleEvent:eventCode stream:aStream];</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="void-didConnect"><a href="#void-didConnect" class="headerlink" title="- (void)didConnect;"></a>- (void)didConnect;</h5><p>websocket 连接请求</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">- (void)didConnect &#123;</span><br><span class="line">    CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR(&quot;GET&quot;), (__bridge CFURLRef)_url, kCFHTTPVersion1_1);</span><br><span class="line">    </span><br><span class="line">    // Set host first so it defaults</span><br><span class="line">    CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Host&quot;), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@&quot;%@:%@&quot;, _url.host, _url.port] : _url.host));</span><br><span class="line">        </span><br><span class="line">    NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];</span><br><span class="line">    SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);</span><br><span class="line">    </span><br><span class="line">    if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) &#123;</span><br><span class="line">        _secKey = [keyBytes base64EncodedStringWithOptions:0];</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        _secKey = [keyBytes base64Encoding];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    // Apply cookies if any have been provided</span><br><span class="line">    NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]];</span><br><span class="line">    for (NSString * cookieKey in cookies) &#123;</span><br><span class="line">        NSString * cookieValue = [cookies objectForKey:cookieKey];</span><br><span class="line">        if ([cookieKey length] &amp;&amp; [cookieValue length]) &#123;</span><br><span class="line">            CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    // set header for http basic auth</span><br><span class="line">    if (_url.user.length &amp;&amp; _url.password.length) &#123;</span><br><span class="line">        NSData *userAndPassword = [[NSString stringWithFormat:@&quot;%@:%@&quot;, _url.user, _url.password] dataUsingEncoding:NSUTF8StringEncoding];</span><br><span class="line">        NSString *userAndPasswordBase64Encoded;</span><br><span class="line">        if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) &#123;</span><br><span class="line">            userAndPasswordBase64Encoded = [userAndPassword base64EncodedStringWithOptions:0];</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            userAndPasswordBase64Encoded = [userAndPassword base64Encoding];</span><br><span class="line">        &#125;</span><br><span class="line">        _basicAuthorizationString = [NSString stringWithFormat:@&quot;Basic %@&quot;, userAndPasswordBase64Encoded];</span><br><span class="line">        CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Authorization&quot;), (__bridge CFStringRef)_basicAuthorizationString);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Upgrade&quot;), CFSTR(&quot;websocket&quot;));</span><br><span class="line">    CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Connection&quot;), CFSTR(&quot;Upgrade&quot;));</span><br><span class="line">    CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Sec-WebSocket-Key&quot;), (__bridge CFStringRef)_secKey);</span><br><span class="line">    CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Sec-WebSocket-Version&quot;), (__bridge CFStringRef)[NSString stringWithFormat:@&quot;%ld&quot;, (long)_webSocketVersion]);</span><br><span class="line">    </span><br><span class="line">    CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Origin&quot;), (__bridge CFStringRef)_url.SR_origin);</span><br><span class="line">    </span><br><span class="line">    if (_requestedProtocols) &#123;</span><br><span class="line">        CFHTTPMessageSetHeaderFieldValue(request, CFSTR(&quot;Sec-WebSocket-Protocol&quot;), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@&quot;, &quot;]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) &#123;</span><br><span class="line">        CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);</span><br><span class="line">    &#125;];</span><br><span class="line">    </span><br><span class="line">    NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request));</span><br><span class="line">    </span><br><span class="line">    CFRelease(request);</span><br><span class="line">		// 通过输入输出流发起连接</span><br><span class="line">    [self _writeData:message];</span><br><span class="line">    //读取 HTTP 响应头</span><br><span class="line">    [self _readHTTPHeader];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="void-readHTTPHeader"><a href="#void-readHTTPHeader" class="headerlink" title="- (void)_readHTTPHeader;"></a>- (void)_readHTTPHeader;</h5><p>循环读取HTTP头部数据，分隔符 <code>\r\n\r\n</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">- (void)_readHTTPHeader &#123;</span><br><span class="line">    if (_receivedHTTPHeaders == NULL) &#123;</span><br><span class="line">        _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self,  NSData *data) &#123;</span><br><span class="line">        CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);</span><br><span class="line">        if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) &#123;</span><br><span class="line">            //读取到头部</span><br><span class="line">            [self _HTTPHeadersDidFinish];</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            [self _readHTTPHeader];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="void-HTTPHeadersDidFinish"><a href="#void-HTTPHeadersDidFinish" class="headerlink" title="- (void)_HTTPHeadersDidFinish;"></a>- (void)_HTTPHeadersDidFinish;</h3><p>判断连接是否成功</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">- (void)_HTTPHeadersDidFinish &#123;</span><br><span class="line">    NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders);</span><br><span class="line">    // key 校验</span><br><span class="line">    if(![self _checkHandshake:_receivedHTTPHeaders]) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR(&quot;Sec-WebSocket-Protocol&quot;)));</span><br><span class="line">    if (negotiatedProtocol) &#123;</span><br><span class="line">        // Make sure we requested the protocol</span><br><span class="line">        if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) &#123;</span><br><span class="line">            //子协议校验</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        _protocol = negotiatedProtocol;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    self.readyState = SR_OPEN;</span><br><span class="line">    </span><br><span class="line">    if (!_didFail) &#123;</span><br><span class="line">    		// 连接成功了，开始接收数据</span><br><span class="line">        [self _readFrameNew];</span><br><span class="line">    &#125;</span><br><span class="line">		// 回调给引用层</span><br><span class="line">    [self _performDelegateBlock:^&#123;</span><br><span class="line">        if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) &#123;</span><br><span class="line">            [self.delegate webSocketDidOpen:self];</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="数据流接收流程"><a href="#数据流接收流程" class="headerlink" title="数据流接收流程"></a>数据流接收流程</h3><h5 id="void-safeHandleEvent-NSStreamEvent-eventCode-stream-NSStream-aStream"><a href="#void-safeHandleEvent-NSStreamEvent-eventCode-stream-NSStream-aStream" class="headerlink" title="- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream"></a>- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream</h5><p>这里会将输入流中的数据读取到<code>_readBuffer</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream &#123;</span><br><span class="line">  switch (eventCode) &#123;</span><br><span class="line">      case NSStreamEventOpenCompleted: &#123;</span><br><span class="line">          // didConnect fires after certificate verification if we&#x27;re using pinned certificates.</span><br><span class="line">          BOOL usingPinnedCerts = [[_urlRequest SR_SSLPinnedCertificates] count] &gt; 0;</span><br><span class="line">          if ((!_secure || !usingPinnedCerts) &amp;&amp; self.readyState == SR_CONNECTING &amp;&amp; aStream == _inputStream) &#123;</span><br><span class="line">              [self didConnect];</span><br><span class="line">          &#125;</span><br><span class="line">          [self _pumpWriting];</span><br><span class="line">          [self _pumpScanner];</span><br><span class="line">          break;</span><br><span class="line">      &#125;</span><br><span class="line">      case NSStreamEventErrorOccurred: &#123;</span><br><span class="line">          break;</span><br><span class="line">      &#125;</span><br><span class="line">      case NSStreamEventEndEncountered: &#123;</span><br><span class="line">          break;</span><br><span class="line">      &#125;</span><br><span class="line">			// 接收到数据</span><br><span class="line">      case NSStreamEventHasBytesAvailable: &#123;</span><br><span class="line">          const int bufferSize = 2048;</span><br><span class="line">          uint8_t buffer[bufferSize];</span><br><span class="line">          while (_inputStream.hasBytesAvailable) &#123;</span><br><span class="line">              NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize];</span><br><span class="line">              if (bytes_read &gt; 0) &#123;</span><br><span class="line">                  [_readBuffer appendBytes:buffer length:bytes_read];</span><br><span class="line">              &#125; else if (bytes_read &lt; 0) &#123;</span><br><span class="line">                  [self _failWithError:_inputStream.streamError];</span><br><span class="line">              &#125;</span><br><span class="line">              if (bytes_read != bufferSize) &#123;</span><br><span class="line">                  break;</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;;</span><br><span class="line">          [self _pumpScanner];//开始扫描</span><br><span class="line">          break;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      case NSStreamEventHasSpaceAvailable: &#123;</span><br><span class="line">      		// 写数据时，没有写完的数据这里会继续写</span><br><span class="line">          [self _pumpWriting];</span><br><span class="line">          break;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">-(void)_pumpScanner &#123;</span><br><span class="line">    if (!_isPumping) &#123;</span><br><span class="line">        _isPumping = YES;</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    // 循环读取消费者需要的数据,如果没有数据则返回NO</span><br><span class="line">    while ([self _innerPumpScanner]) &#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    _isPumping = NO;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="BOOL-innerPumpScanner"><a href="#BOOL-innerPumpScanner" class="headerlink" title="- (BOOL)_innerPumpScanner"></a>- (BOOL)_innerPumpScanner</h3><p>读取到消费者需要的数据</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">- (BOOL)_innerPumpScanner &#123;</span><br><span class="line">	//如果消费者为空，则直接返回</span><br><span class="line">	//读到消费者需要的数据长度时，返回TRUE</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="数据读取流程"><a href="#数据读取流程" class="headerlink" title="数据读取流程"></a>数据读取流程</h3><h5 id="void-readFrameNew"><a href="#void-readFrameNew" class="headerlink" title="- (void)_readFrameNew;"></a>- (void)_readFrameNew;</h5><p>清空上一帧上一帧数据，随后调用 <code>_readFrameContinue</code></p>
<h4 id="void-readFrameContinue"><a href="#void-readFrameContinue" class="headerlink" title="- (void)_readFrameContinue;"></a>- (void)_readFrameContinue;</h4><ol>
<li>添加一个消费者</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">//添加消费者，用一个指定的长度，是否读到当前帧</span><br><span class="line">- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes &#123;</span><br><span class="line">    //添加到消费者队列</span><br><span class="line">    [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]];</span><br><span class="line">    //扫描消费者所需要的字节数</span><br><span class="line">    [self _pumpScanner];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">- (void)_readFrameContinue &#123;</span><br><span class="line">		// 添加一个消费者</span><br><span class="line">    [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) &#123;</span><br><span class="line">        __block frame_header header = &#123;0&#125;;</span><br><span class="line">        const uint8_t *headerBuffer = data.bytes;</span><br><span class="line">        uint8_t receivedOpcode = (SROpCodeMask &amp; headerBuffer[0]);</span><br><span class="line">        BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose);</span><br><span class="line">        </span><br><span class="line">        header.opcode = receivedOpcode == 0 ? self-&gt;_currentFrameOpcode : receivedOpcode;</span><br><span class="line">        header.fin = !!(SRFinMask &amp; headerBuffer[0]);</span><br><span class="line">        header.masked = !!(SRMaskMask &amp; headerBuffer[1]);</span><br><span class="line">        header.payload_length = SRPayloadLenMask &amp; headerBuffer[1];</span><br><span class="line">        </span><br><span class="line">        headerBuffer = NULL;</span><br><span class="line">        size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;</span><br><span class="line">        if (header.payload_length == 126) &#123;</span><br><span class="line">            extra_bytes_needed += sizeof(uint16_t);</span><br><span class="line">        &#125; else if (header.payload_length == 127) &#123;</span><br><span class="line">            extra_bytes_needed += sizeof(uint64_t);</span><br><span class="line">        &#125;</span><br><span class="line">        if (extra_bytes_needed == 0) &#123;</span><br><span class="line">        		// 不需要读取扩展字段，则开始读取数据段</span><br><span class="line">            [self _handleFrameHeader:header curData:self-&gt;_currentFrameData];</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">        		// 读取扩展字段</span><br><span class="line">            [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) &#123;</span><br><span class="line">                size_t mapped_size = data.length;</span><br><span class="line">                const void *mapped_buffer = data.bytes;</span><br><span class="line">                size_t offset = 0;</span><br><span class="line">                if (header.payload_length == 126) &#123;</span><br><span class="line">                    uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer));</span><br><span class="line">                    header.payload_length = newLen;</span><br><span class="line">                    offset += sizeof(uint16_t);</span><br><span class="line">                &#125; else if (header.payload_length == 127) &#123;</span><br><span class="line">                    header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer));</span><br><span class="line">                    offset += sizeof(uint64_t);</span><br><span class="line">                &#125;</span><br><span class="line">                </span><br><span class="line">                if (header.masked) &#123;</span><br><span class="line">                    memcpy(self-&gt;_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self-&gt;_currentReadMaskKey));</span><br><span class="line">                &#125;</span><br><span class="line">                // 读取到扩展字段后，再读取 Data 数据</span><br><span class="line">                [self _handleFrameHeader:header curData:self-&gt;_currentFrameData];</span><br><span class="line">            &#125; readToCurrentFrame:NO unmaskBytes:NO];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; readToCurrentFrame:NO unmaskBytes:NO];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>读取帧的数据段</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData &#123;    </span><br><span class="line">    BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose);</span><br><span class="line">    </span><br><span class="line">    if (!isControlFrame) &#123;</span><br><span class="line">        _currentFrameOpcode = frame_header.opcode;</span><br><span class="line">        _currentFrameCount += 1;</span><br><span class="line">    &#125;</span><br><span class="line">    //如果数据长度为0</span><br><span class="line">    if (frame_header.payload_length == 0) &#123;</span><br><span class="line">        if (isControlFrame) &#123;</span><br><span class="line">        		// 数据读完，开始处理数据</span><br><span class="line">            [self _handleFrameWithData:curData opCode:frame_header.opcode];</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            if (frame_header.fin) &#123;</span><br><span class="line">            		// 数据读完，开始处理数据</span><br><span class="line">                [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode];</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                // TODO add assert that opcode is not a control;</span><br><span class="line">                // 数据还没读完，据需读取数据</span><br><span class="line">                [self _readFrameContinue];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">    		// 读取数据，回调后的处理流程同上</span><br><span class="line">        [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) &#123;</span><br><span class="line">            if (isControlFrame) &#123;</span><br><span class="line">                [self _handleFrameWithData:newData opCode:frame_header.opcode];</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                if (frame_header.fin) &#123;</span><br><span class="line">                    [self _handleFrameWithData:self-&gt;_currentFrameData opCode:frame_header.opcode];</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    // TODO add assert that opcode is not a control;</span><br><span class="line">                    [self _readFrameContinue];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>开始处理数据</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode &#123;                </span><br><span class="line">    BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose);</span><br><span class="line">    if (!isControlFrame) &#123;</span><br><span class="line">    		// 里面是异步读取数据帧，跟下面的区别就是，非数据帧，需要先清理数据才能继续读取</span><br><span class="line">        [self _readFrameNew];</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        dispatch_async(_workQueue, ^&#123;</span><br><span class="line">        		// 数据帧不需要清理数据，直接读取</span><br><span class="line">            [self _readFrameContinue];</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    //frameData will be copied before passing to handlers</span><br><span class="line">    //otherwise there can be misbehaviours when value at the pointer is changed</span><br><span class="line">    // 开始处理数据，到这里整个接收流程也就走完了</span><br><span class="line">    switch (opcode) &#123;</span><br><span class="line">        case SROpCodeTextFrame: &#123;</span><br><span class="line">            if ([self.delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] &amp;&amp; ![self.delegate webSocketShouldConvertTextFrameToString:self]) &#123;</span><br><span class="line">                [self _handleMessage:[frameData copy]];</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding];</span><br><span class="line">                if (str == nil &amp;&amp; frameData) &#123;</span><br><span class="line">                    [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@&quot;Text frames must be valid UTF-8&quot;];</span><br><span class="line">                    dispatch_async(_workQueue, ^&#123;</span><br><span class="line">                        [self closeConnection];</span><br><span class="line">                    &#125;);</span><br><span class="line">                    return;</span><br><span class="line">                &#125;</span><br><span class="line">                [self _handleMessage:str];</span><br><span class="line">            &#125;</span><br><span class="line">            break;</span><br><span class="line">        &#125;</span><br><span class="line">        case SROpCodeBinaryFrame:</span><br><span class="line">            [self _handleMessage:[frameData copy]];</span><br><span class="line">            break;</span><br><span class="line">        case SROpCodeConnectionClose:</span><br><span class="line">            [self handleCloseWithData:[frameData copy]];</span><br><span class="line">            break;</span><br><span class="line">        case SROpCodePing:</span><br><span class="line">            [self handlePing:[frameData copy]];</span><br><span class="line">            break;</span><br><span class="line">        case SROpCodePong:</span><br><span class="line">            [self handlePong:[frameData copy]];</span><br><span class="line">            break;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h3 id="发送数据流程"><a href="#发送数据流程" class="headerlink" title="发送数据流程"></a>发送数据流程</h3><ol>
<li><ul>
<li>(void)send:(id)data;</li>
</ul>
</li>
<li><ul>
<li>(void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;</li>
</ul>
</li>
<li><ul>
<li>(void)_writeData:(NSData *)data;</li>
</ul>
</li>
<li><ul>
<li>(void)_pumpWriting; &#x2F;&#x2F; 开始写数据，数据量很大的话，单次写不完，会在流回调中继续写数据</li>
</ul>
</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">- (void)_pumpWriting &#123;</span><br><span class="line">    NSUInteger dataLength = _outputBuffer.length;</span><br><span class="line">    if (dataLength - _outputBufferOffset &gt; 0 &amp;&amp; _outputStream.hasSpaceAvailable) &#123;</span><br><span class="line">        // 这里写数据不一定全部写完</span><br><span class="line">        NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset];</span><br><span class="line">        if (bytesWritten == -1) &#123;</span><br><span class="line">             return;</span><br><span class="line">        &#125;</span><br><span class="line">        //表示已经写入的大小</span><br><span class="line">        _outputBufferOffset += bytesWritten;</span><br><span class="line">        //超过一定值，则重置</span><br><span class="line">        if (_outputBufferOffset &gt; 4096 &amp;&amp; _outputBufferOffset &gt; (_outputBuffer.length &gt;&gt; 1)) &#123;</span><br><span class="line">            //更新偏移量，重新生成新的buffer</span><br><span class="line">            _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset];</span><br><span class="line">            _outputBufferOffset = 0;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>两个线程，一个负责生产，一个负责消费，这种生产者-消费者模式运用的很巧妙。<br>NSMutableData 运用的很好，避免了频繁生成 NSMutableData 对象；</p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/WebSocket/" rel="tag"># WebSocket</a>
              <a href="/tags/SRWebSocket/" rel="tag"># SRWebSocket</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2021/05/iOS/Modules/Modules/" rel="prev" title="理解 iOS 中的 Modules">
                  <i class="fa fa-chevron-left"></i> 理解 iOS 中的 Modules
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2022/01/NetworkProtocol/WebSocket/WebSocket/" rel="next" title="WebSocket">
                  WebSocket <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 2015 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">vhuichen</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/pisces/" rel="noopener" target="_blank">NexT.Pisces</a>
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
